/*
* @Author: Administrator
* @Date:   2021-08-28 10:59:28
* @Last Modified by:   Administrator
* @Last Modified time: 2021-08-30 20:17:55
*/

//////////
//语法解析器 //
//////////

var Let = require('./let.js');



//多叉树节点
class Node{
	constructor(props){
		this.tokenLiteral="";
	}
	getLiteral(){
		return this.tokenLiteral
	}
}

//
class Statement extends Node{
	StatementNode(){
		return this;
	}
}

//用来抽象等号右边的表达式(例：let foo=1+2*bar;)
/**
 * Expression用来表示可以放在等号后面进行赋值的代码字符串，它可以是一个数字，一个变量字符串，也可以是一串复杂的算术表达式
 * (例：let age=18; let foo=1+2*bar;)
 * LetStatement := LET IDENTIFIER ASSIGN_SIGN EXPRESSION SEMI (这种语法表达式也叫Backus-Naur 范式)
 */
class Expression extends Node{
	constructor(props){
		super(props);
		this.tokenLiteral=props.token.getLiteral();
	}
	expressionNode(){
		return this;
	}
}

/**
 * 表示等号左边的变量foo
 * (例：let foo=123;)IDENTIFIER
 * LetStatement := LET IDENTIFIER ASSIGN_SIGN EXPRESSION SEMI
 */
class Identifier extends Expression{
	constructor(props){
		super(props);
		this.tokenLiteral=props.token.getLiteral();
		this.token=props.token;
		this.value=""
	}
}


/**
 * 整个程序所有语句代码的抽象语法树(多叉树)
 * Program 类是对整个程序代码的抽象，它由一系列Statement组成，Statement基本可以理解为一句以分号结束的代码。于是整个程序就是由很多条以分号结束的语句代码的集合。当然有一些不已分号结束的语句也是Statement，例如：
 * let foo=1+2*bar;
 * if (x == 10) {...}
 * else {...}
 */
class Program{
	constructor(){
		this.statements=[];
	}
	getLiteral(){
		if(this.statements.length>0){
			return this.statements[0].tokenLiteral();
		}else{
			return "";
		}
	}
}


class SyntaxParser{
	//构造函数(传入词法解析器lexer)
	constructor(lexer){
		this.lexer=lexer;
		//执行词法分析
		this.lexer.lexing();
		this.tokenPos=0;//token在数组中的位置
		//当前token 例：{ TokenType: 0, literal: 'let', lineNumber: 0 }
		this.currToken=null;
		//下一个token 例：{ TokenType: 0, literal: 'x', lineNumber: 0 }
		this.nextToken=null;

		this.readToken();//读取token
		this.readToken();//读取token

		//抽象语法树
		this.program=new Program();

		this.LOWEST=0;
		this.EQUALS=1;//==
		this.LESSGREATER=2;//< or >
		this.SUM=3;//加号
		this.PRODUCT=4;//乘法
		this.PREFIX=5;// -x or !x
		this.CALL=6;//函数调用myfunction(x)


	}

	/**
	 * 读取token
	 * let x=y+5;
	 * 一次必须读入两个token，这样我们才了解当前解析代码的意图例如假设当前解析的代码是5；
	 * 那么peekToken就对应的就是分号，
	 * 这样解析器就知道当前解析的代码表示一个整数
	 */
	readToken(){
		this.currToken=this.nextToken;
		//tokens为token数组
		this.nextToken=this.lexer.tokens[this.tokenPos];
		this.tokenPos++;
	}

	//读取token数组
	parseProgram(){
		console.log("============= start CompilerParser =============");
		console.log(" ")
		/**
		 * 判断当前token是否到文本末尾终结符
		 */
		while(this.curToken.getType()!==this.lexer.EOF){
			//调用parseStatement来解析一条以语句。
			var stmt=this.checkStatementType();
			if(stmt!==null){
				this.program.statements.push(stmt);
			}
			//读取下一个token
			this.readToken();
		}
		console.log(" ")
		// console.log("============= end CompilerParser =============");
		// console.log(this.program)
		return this.program
	}

	/**
	 * 检查语句类型
	 * parseStatement中，它会根据当前读入的token类型来进行不同的操作，
	 * 如果读到的当前token是一个关键字let, 那意味着，解析器当前读到了一条以let开始的变量定义语句，
	 * 于是解析器接下来就要检测后面一系列token的组合关系是否符合let 语句语法表达式指定的规范，
	 * 负责这个检测任务的就是函数parseLetStatement()。
	 */
	checkStatementType(){
		switch(this.curToken.getType()){
			case this.lexer.LET:
				//变量定义
			    return this.parseLetStatement();
			case this.lexer.RETURN:
				//return
			    return this.parseReturnStatement();
			default:
				//表达式
			    return this.parseExpressionStatement();
		}
	}

	/**
	 * token的组合关系是否符合let 语句语法表达式指定的规范
	 * parseLetStatement函数的实现逻辑严格遵守语法表达式的规定。
	 * LetStatement := LET IDENTIFIER ASSIGN_SIGN EXPRESSION SEMI
	 */
	parseLetStatement(){
		/**
		 * 以分号结束的代码
		 * let x=123;
		 * let y=1234;
		 *
			[
				Token { TokenType: 0, literal: 'let', lineNumber: 0 },
				Token { TokenType: 1, literal: 'x', lineNumber: 0 },
				Token { TokenType: 2, literal: '=', lineNumber: 0 },
				Token { TokenType: 4, literal: '123', lineNumber: 0 },
				Token { TokenType: 5, literal: ';', lineNumber: 0 },

				Token { TokenType: 0, literal: 'let', lineNumber: 0 },
				Token { TokenType: 1, literal: 'y', lineNumber: 1 },
				Token { TokenType: 2, literal: '=', lineNumber: 1 },
				Token { TokenType: 4, literal: '1234', lineNumber: 1 },
				Token { TokenType: 5, literal: ';', lineNumber: 1 }
			]
		 */

		var props={};
		props.token=this.curToken;
		/**
		 * 判断下一个token中的let右边是否为变量（判断let后边的x是否为变量）
		 * this.curToken为{ TokenType: 0, literal: 'let', lineNumber: 0 }
		 */
		if(!this.expectPeek(this.lexer.IDENTIFIER)){
			return null;
		}

		//等号左边的变量
		var identProps={};
		identProps.token=this.curToken;
		identProps.value=this.curToken.getLiteral();
		props.identifer=new Identifier(identProps);
		/**
		 * 判断下一个token中的x右边是否为等号
		 * 执行this.expectPeek后token为{ TokenType: 2, literal: '=', lineNumber: 0 }
		 */
		if(!this.expectPeek(this.lexer.ASSIGN_SIGN)){
			return null;
		}
		/**
		 * 判断下一个token中的=右边是否为数字
		 * 执行this.expectPeek后token为{ TokenType: 4, literal: '123', lineNumber: 0 }
		 */
		if(!this.expectPeek(this.lexer.INTEGER)){
			return null;
		}

		//获取等号右边的表达式或变量值
		var exprProps={};
		exprProps.token=this.curToken;
		props.expression=new Expression(exprProps);
		/**
		 * 判断下一个token中的123右边是否为分号
		 * 执行this.expectPeek后token为{ TokenType: 5, literal: ';', lineNumber: 0 }
		 */
		if(!this.expectPeek(this.lexer.SEMICOLON)){
			return null;
		}

		// console.log("parseLetStatement:",props);
		var letStatement =new LetStatement(props);
		return letStatement;
	}
	parseReturnStatement(){
		var props={};
		props.token=this.curToken;
		if(!this.expectPeek(this.lexer.INTEGER)){
			return null;
		}

		//获取等号右边的表达式或变量值
		var exprProps={};
		exprProps.token=this.curToken;
		props.expression=new Expression(exprProps);
		if(!this.expectPeek(this.lexer.SEMICOLON)){
			return null;
		}

		return new ReturnStatement(props);
	}
	//判断当前token的类型是否相等
	curTokenIs(tokenType){
		return this.curToken.getType()===tokenType;
	}

	//判断下一个token的类型是否相等
	peekTokenIs(tokenType){
		return this.peekToken.getType()===tokenType;
	}

	/*
	 *判断token的类型
	 */
	expectPeek(tokenType){
		console.log("expectPeek:");
		console.log("expectPeek:",tokenType);
		console.log("expectPeek:",this.peekToken.getType());
		if(this.peekTokenIs(tokenType)){
			//读取下一个token
			this.nextToken();
			return true;
		}else{
			console.log("expectPeek1:");
			return false;
		}
	}

	//解析表达式语句
	parseExpressionStatement(){
		var props={};
		props.token=this.curToken;
		props.expression=this.parseExpression(this.LOWEST);
		var stmt=new ExpressionStatement(props);
		//判断下一个token的类型
		if(this.peekTokenIs(this.lexer.SEMICOLON)){
			this.nextToken();
		}
		return stmt;
	}
	//解析表达式
	parseExpression(precedene){
		console.log('start 解析表达式 parseExpression:');
		console.log(this.curToken.getType());
		console.log(this.prefixParseFns);
		//判断前序表达式是否存在，prefixParseFns数组中是否存在当前curToken的信息
		var prefix=this.prefixParseFns[this.curToken.getType()];
		//就是这里返回就变成空的了
		console.log(prefix);
		if(prefix===null){
			return null;
		}
		var leftExp=prefix(this);
		console.log("leftExp1:",leftExp);
		console.log("curToken:",this.curToken);
		console.log("precedene:",precedene);
		console.log("下一个token是不结尾:",this.peekTokenIs(this.lexer.SEMICOLON));
		console.log("下一个token的中的类型是否存在(加，减，乘，除，函数):",this.peekPrecedence());
		/**
		 * 第一个表达式用于判断下一个token是不结尾
		 * 第二个表达式用于判断下一个token的中的类型是否存在(加，减，乘，除，函数)
		 */
		if(this.peekTokenIs(this.lexer.SEMICOLON)!==true && precedene<this.peekPrecedence()){
			//获取下一个token
			console.log('进入了')
			//获取下一个token中的信息
			var infix=this.infixParseFns[this.peekToken.getType()];
			if(infix===null){
				return leftExp;
			}
			this.nextToken();
			//解析中序表达式
			leftExp=infix(this,leftExp)
			console.log("this.peekToken.getType():",this.peekToken.getType());
			console.log("leftExp2:",leftExp);
		}
			console.log(leftExp);
		console.log('end 解析表达式 parseExpression:');
		return leftExp;
	}
	//caller表示this
	parseIdentifier(caller){
		return caller.createIdentifier();
	}
	//解析整型数字
	parseIntegerLiteral(caller){
		console.log(caller.curToken)
		var intProps={};
		intProps.token=caller.curToken;
		intProps.value=parseInt(caller.curToken.getLiteral());
		if(intProps.value===NaN){
			return null;
		}
		return new IntegerLiteral(intProps);
	}
	createIdentifier(){
		var identProps={};
		identProps.token=this.curToken;
		identProps.value=this.curToken.getLiteral();
		return new Identifier(identProps);
	}

	parsePrefixExpression(caller){
		var props={};
		props.token=caller.curToken;
		props.operator=caller.curToken.getLiteral();
		caller.nextToken();
		props.expression=caller.parseExpression(caller.PREFIX)
		return new PrefixExpression(props);
	}







}

// export default CompilerParser;

module.exports=SyntaxParser;